import java.awt.Font;
import java.awt.GridLayout;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Random;

import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;


public class FortyTwoApplet extends JApplet {
    /**
	 * 
	 */
	private static final long serialVersionUID = -158803687991457095L;
	//Called when this applet is loaded into the browser.
	JTextPane genome1display;
	JTextPane genome2display;
	JTextPane genome3display;
	JTextPane genome4display;

	ArrayList<Genome> genomes;
	
    public void init() {
        //Execute a job on the event-dispatching thread; creating this applet's GUI.
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                	JPanel pane = new JPanel();
                	GridLayout layout = new GridLayout(4,1);
                	pane.setLayout(layout);
                	
                    genome1display = new JTextPane();
                    genome1display.setFont(new Font("Monospaced", Font.PLAIN, 12));
                    pane.add(genome1display);
                    genome2display = new JTextPane();
                    genome2display.setFont(new Font("Monospaced", Font.PLAIN, 12));
                    pane.add(genome2display);
                    genome3display = new JTextPane();
                    genome3display.setFont(new Font("Monospaced", Font.PLAIN, 12));
                    pane.add(genome3display);
                    genome4display = new JTextPane();
                    genome4display.setFont(new Font("Monospaced", Font.PLAIN, 12));
                    pane.add(genome4display);
                    
                    add(pane);
                    setSize(600, 300);
                }
            });
        } catch (Exception e) {
            System.err.println("createGUI didn't complete successfully");
        }
    }
    
    public void start()
    {
    	spawnGenomes();
    	Thread start = new Thread(process);
    	start.start();
    }
    
    public void spawnGenomes()
    {
    	genomes = new ArrayList<Genome>(4);
    	genomes.add(new Genome());
    	genomes.add(new Genome());
    	genomes.add(new Genome());
    	genomes.add(new Genome());
    	genomes.get(0).genome = new byte[10];
    	genomes.get(1).genome = new byte[10];
    	genomes.get(2).genome = new byte[10];
    	genomes.get(3).genome = new byte[10];
    	
		Random gen = new Random();
		gen.nextBytes(genomes.get(0).genome);
		gen.nextBytes(genomes.get(1).genome);
		gen.nextBytes(genomes.get(2).genome);
		gen.nextBytes(genomes.get(3).genome);
    }
    
    Runnable process = new Runnable()
    {
    	public void run()
    	{
    		boolean cont = true;
    		while(cont)
    		{
    			for (int i = 0; i < genomes.size();i++)
    			{
    				genomes.get(i).sum = sum(genomes.get(i).genome);
    				genomes.get(i).fitness = Math.abs(42-genomes.get(i).sum);
    			}

				genome1display.setText(buildDisplay(genomes.get(0).genome,genomes.get(0).sum));
				genome2display.setText(buildDisplay(genomes.get(1).genome,genomes.get(1).sum));
				genome3display.setText(buildDisplay(genomes.get(2).genome,genomes.get(2).sum));
				genome4display.setText(buildDisplay(genomes.get(3).genome,genomes.get(3).sum));
				
    			Collections.sort(genomes, new Comparator<Genome>(){

					@Override
					public int compare(Genome arg0, Genome arg1) {
						return arg0.fitness - arg1.fitness;
					}});
    			
    			
    			
    			if(genomes.get(0).fitness == 0)
    			{
    				cont = false;
    			}
    			else
    			{
    			try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    			Genome[] children = Crossover(genomes.get(0),genomes.get(1));
    			genomes.set(0,children[0]);
    			genomes.set(1,children[1]);
    			children = Crossover(genomes.get(0),genomes.get(2));
    			genomes.set(2,children[0]);
    			genomes.set(3,children[1]);
    			}
    		
    		}
    		genome1display.setText(buildDisplay(genomes.get(0).genome,genomes.get(0).sum));
    		genome2display.setText("");
    		genome3display.setText("");
    		genome4display.setText("");
    		
    		
    	}
    };
    
    public Genome[] Crossover(Genome a, Genome b)
    {
    	String aStr = byteArrayToString(a.genome);
    	String bStr = byteArrayToString(b.genome);
    	int point1 = (int) (Math.random() * (aStr.length()-5));
    	int point2 = (int) (Math.random() * (aStr.length()-5));
    	
    	if(point1 > point2)
    	{
    		int temp = point2;
    		point2 = point1;
    		point1 = temp;
    	}
    	else if(point1 == point2)
    	{
    		
    	}
    	
    	String childA = aStr.substring(0, point1) + bStr.substring(point1,point2) + aStr.substring(point2);
    	String childB = bStr.substring(0, point1) + aStr.substring(point1,point2) + bStr.substring(point2);
    	
    	if(childA.charAt(point1)=='1')
    	{
    		childA= childA.substring(0, point1)+"0"+childA.substring(point1+1);
    	}
    	else
    	{
    		childA= childA.substring(0, point1)+"1"+childA.substring(point1+1);
    	}
    	if(childA.charAt(point2)=='1')
    	{
    		childA= childA.substring(0, point2)+"0"+childA.substring(point2+1);
    	}
    	else
    	{
    		childA= childA.substring(0, point2)+"1"+childA.substring(point2+1);
    	}
    	
    	if(childB.charAt(point1)=='1')
    	{
    		childB= childB.substring(0, point1)+"0"+childB.substring(point1+1);
    	}
    	else
    	{
    		childB= childB.substring(0, point1)+"1"+childB.substring(point1+1);
    	}
    	if(childB.charAt(point2)=='1')
    	{
    		childB= childB.substring(0, point2)+"0"+childB.substring(point2+1);
    	}
    	else
    	{
    		childB= childB.substring(0, point2)+"1"+childB.substring(point2+1);
    	}
    	
    	Genome A = new Genome();
    	A.genome = stringToByteArray(childA);
    	Genome B = new Genome();
    	B.genome = stringToByteArray(childB);
		return new Genome[]{A,B};
    	
    }
    
    public byte[] stringToByteArray(String str)
    {
    	byte[] bytes = new byte[str.length()/8];
    	for(int i = 0; i < bytes.length;i++)
    	{
    		BigInteger bi = new BigInteger(str.substring(i*8, (i+1)*8), 2);
    		bytes[i] = bi.byteValue();
    	}
    	
		return bytes;
    	
    }
    
    public String byteArrayToString(byte[] array)
    {
    	String out = "";
    	for(int i = 0; i < array.length;i++)
    	{
    		BigInteger bi = new BigInteger(new byte[]{array[i]});
    		String s = bi.toString(2);
    		s = s.replace("-", "");
    		while (s.length() < 8)
    		{
    			s = "0"+s;
    		}
    		out+=s;
    	}
    	return out;
    }
    
    public String buildDisplay(byte[] genome, int fitness)
    {
    	String out = "";
    	out+= byteArrayToString(genome);
    	out+= "\n";
    	
    	for(int i = 0; i < genome.length;i++)
    	{
    		out += Math.abs((int)genome[i])%10 + " ";
    	}
    	out += "= " + fitness + "\t" + (42-fitness);
    	
    	
    	return out;
    }
    
    public int sum(byte[] genome)
    {
    	int sum = 0;
    	for(int i =0; i<genome.length ; i++)
    	{
    		sum += Math.abs((int)genome[i])%10;
    	}
		return sum;
    }
    
    public class Genome
    {
    	public byte[] genome;
    	public int fitness;
    	public int sum;
    
    }
}